﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using Krist_em_up.Interface.Gui;
using Krist_em_up.Graphics;
using Krist_em_up.Music;
using Krist_em_up.Item;
using Krist_em_up.Item.Projectiles;

namespace Krist_em_up.Characters.Enemies
{
    public class Enemy : Character
    {
        public enum state
        {
            Patrolling,
            Shooting,
            Jumping,
        }
        public const int HEIGHT = 48;
        const int WIDTH = 24;
        const int hpMax = 5;
        const float spotRad = 300f;
        int column = 0;
        int row = 1;
        Rectangle SourceRectangle;
        private SpriteEffects effect;
        private Animation anim;
        AnimationPlayer player;
        HealthBar hp;
        TimeSpan lastShot, reloadTimer;
        state etat = state.Patrolling, prevState = state.Patrolling;
        float range, distance;
        Vector2 prevPos;
        public Enemy(Kristemup game, Vector2 position, float range)
            : base(game, position)
        {
            textureAsset = "Characters/claudius";
            speed = new Vector2(2.3f, 0);
            direction = new Vector2(1, 1);
            SourceRectangle = new Rectangle(column * WIDTH, row * HEIGHT, WIDTH, HEIGHT);
            rectangle = new Rectangle((int)position.X, (int)position.Y, HEIGHT, WIDTH);
            texture = game.Content.Load<Texture2D>(textureAsset);
            anim = new Animation(texture, 0.08f, WIDTH, 1, HEIGHT, true);
            player = new AnimationPlayer();
            player.PlayAnimation(anim);
            hp = new HealthBar(game, this, hpMax);
            reloadTimer = new TimeSpan(0, 0, 0, 1, 0);
            lastShot = -reloadTimer;
            distance = 0;
            this.range = range;
        }

        public void Update(GameTime gameTime)
        {
            if (etat == state.Patrolling)
            {
                if (spotted())
                {
                    etat = state.Shooting;
                    prevState = etat;
                }
                else
                {
                    patrolling();
                }
            }
            else
            {
                bool alive = false;
                foreach (Kris kris in game.tabKris)
                {
                    alive |= kris.Hp != 0;
                }
                if (alive)
                {
                    shooting(gameTime);
                }
                else
                {
                    etat = state.Patrolling;
                    patrolling();
                }
            }
            prevPos = position;
            position += speed * direction;
            rectangle = new Rectangle((int)position.X, (int)position.Y, WIDTH, HEIGHT);
            if (Collision())
            {
                position.X = prevPos.X;
                if (etat == state.Patrolling)
                {
                    if (direction.X > 0) distance = 2 * range;
                    else distance = 0;
                }
                else
                {
                    jump();
                }
            }
            fall();
        }


        public void Draw(GameTime gametime, SpriteBatch spriteBatch, Vector2 origin, bool stop)
        {
            player.Stop = stop;
            spriteBatch.Begin();
            hp.DrawBar(spriteBatch, origin);
            player.Draw(gametime, spriteBatch, position - origin, effect);
            spriteBatch.End();
        }


        public void Shoot(TimeSpan gameTime)
        {
            if (lastShot + reloadTimer < gameTime)
            {
                bool reachable = false;
                foreach (Kris kris in game.tabKris)
                {
                    reachable |= (Math.Min(position.Y + HEIGHT, kris.Position.Y + Kris.HEIGHT) > Math.Max(position.Y, kris.Position.Y));
                }
                if (reachable)
                {
                    if (!obstacle((int)direction.X))
                    {
                        GunBullet balle = new GunBullet(-1, game, new Vector2(position.X + WIDTH, position.Y + HEIGHT / 2), new Vector2(direction.X, 0));
                        game.bullets.Add(balle);

                        Sounds.PlayShoot();
                        lastShot = gameTime;
                    }
                }
            }
        }
        public bool obstacle(int dx)
        {
            if ((int)posPlat().X == (int)choixKrisX() / Plateforms.Plateform.Width
                || (int)choixKrisX() / Plateforms.Plateform.Width < 0 || (int)choixKrisX() / Plateforms.Plateform.Width > game.map.Plateforms.GetLength(0))
                return false;
            else
            {
                int i = (int)posPlat().X;
                while (i != (int)(choixKrisX()
                    / Plateforms.Plateform.Width) && (game.map.Plateforms[i, (int)posPlat().Y].Collision
                    == Krist_em_up.Plateforms.TileCollision.Passable))
                {
                    i += dx;
                }
                return (i != (int)choixKrisX() / Plateforms.Plateform.Width);
            }
        }
        /// <summary>
        /// Check if he spotted a Kris
        /// </summary>
        /// <returns>Whether or not a Kris is spotted in the area</returns>
        public bool spotted()
        {
            bool spoty, spotx;
            spotx = Math.Abs(choixKrisX() - position.X) < spotRad;
            spoty = Math.Min(position.Y + HEIGHT, choixKrisY() + Kris.HEIGHT) > Math.Max(position.Y, choixKrisY());
            return (spotx & spoty);
        }

        /// <summary>
        /// When a guy is hit by an opponent bullet
        /// </summary>
        /// <param name="Enemies"></param>
        public void estTouche(LinkedList<Enemy> Enemies, int shooter)
        {
            hp.hurt(shooter);
            game.score += 10;
            if (hp.getHp() <= 0)
            {
                drop();
                Enemies.Remove(this);
                game.score += 50;
            }
        }
        /// <summary>
        /// Randomly drop bonuses
        /// </summary>
        public void drop()
        {
            Random R = new Random();
            switch (R.Next(1, 4))
            {
                case 1:
                    game.SpawnP(posPlat().X, posPlat().Y);
                    break;
                case 2:
                    game.SpawnS(posPlat().X, posPlat().Y);
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Handle collision with the plateforms on x-axis
        /// </summary>
        /// <returns>whether or not there is a collision on X-axis</returns>
        private bool Collision()
        {
            if (direction.X < 0)
            {
                return (game.map.Plateforms[(int)posPlat().X, (int)posPlat().Y].Collision
                == Plateforms.TileCollision.Impassable);
            }
            else
            {
                
                return (game.map.Plateforms[(int)((position.X + WIDTH) / Plateforms.Plateform.Width), (int)posPlat().Y].Collision
                == Plateforms.TileCollision.Impassable);
            }
        }

        /// <summary>
        /// Handle pattroling while in patrol mode
        /// </summary>
        public void patrolling()
        {
            if (distance < range)
            {
                direction.X = 1;
                effect = SpriteEffects.FlipHorizontally;
                distance += direction.X * speed.X;
            }
            else if (Math.Abs(distance - range) < speed.X)
            {
                if (direction.X == 1) distance = 2 * range;
                else distance = 0;
            }
            else
            {
                direction.X = -1;
                effect = SpriteEffects.None;
                distance += direction.X * speed.X;
            }
            try
            {
                if (direction.X == 1)
                {
                    if (game.map.Plateforms[(int)((position.X + direction.X * speed.X + WIDTH) / Plateforms.Plateform.Width), (int)posPlat().Y + 1].Collision
                        == Krist_em_up.Plateforms.TileCollision.Passable)
                    {
                        distance = 2 * range;
                    }
                }
                else
                {
                    if (game.map.Plateforms[(int)((position.X + direction.X * speed.X) / Plateforms.Plateform.Width), (int)posPlat().Y + 1].Collision
                        == Krist_em_up.Plateforms.TileCollision.Passable)
                    {
                        distance = 0;
                    }
                }
            }
            catch { }
        
        }
        /// <summary>
        /// "Killing machine" mode : chase the spotted Kris and shoot him when possible
        /// </summary>
        /// <param name="gameTime"></param>
        public void shooting(GameTime gameTime)
        {
            if (choixKrisX() > position.X)
            {
                direction.X = 1;
                effect = SpriteEffects.FlipHorizontally;
                Shoot(gameTime.TotalGameTime);
                player.Stop = false;
            }
            else if (Math.Abs(Position.X - choixKrisX()) < speed.X)
            {
                direction.X = 0;
                player.FrameIndex = 0;
                player.Stop = true;
            }
            else
            {
                direction.X = -1;
                effect = SpriteEffects.None;
                Shoot(gameTime.TotalGameTime);
                player.Stop = false;
                rectangle = new Rectangle((int)position.X, (int)position.Y, anim.FrameWidth, anim.FrameHeight);
            }
        }

        /// <summary>
        /// Handle jump
        /// </summary>
        private void jump()
        {
            if (etat != state.Jumping)
            {
                prevState = etat;
                etat = state.Jumping;
                direction.Y = -1;
                speed.Y = 14;
                if (direction.X == -1) position.X += WIDTH / 2;
                else position.X -= WIDTH / 3;
                position.Y += speed.Y * direction.Y;
            }
        }

        /// <summary>
        /// Handle fake gravity
        /// </summary>
        private void fall()
        {
            if (((int)posPlat().Y + 1 != game.map.Plateforms.GetLength(1)))
            {
                if (!CollisionE())
                {
                    direction.Y = -1;
                    if (CollisionUp())
                    {
                        if (position.Y < (Math.Floor(position.Y / Plateforms.Plateform.Height) + 1) * Plateforms.Plateform.Height)
                        {
                            speed.Y = 0;
                            position.Y++;
                        }
                    }
                    else
                    {
                        speed.Y -= 0.8f;
                    }
                }
                else
                {
                    etat = prevState;
                    direction.Y = 1;
                    speed.Y = 0;
                    position.Y = ((int)(position.Y / Plateforms.Plateform.Height) * Plateforms.Plateform.Height);
                }
            }
            else
            {
                etat = prevState;
                direction.Y = 1;
                speed.Y = 0;
                position.Y = 384;
            }
        }

        public Vector2 posPlat()
        {
            return new Vector2(position.X / Plateforms.Plateform.Width, position.Y / Plateforms.Plateform.Height);
        }
        /// <summary>
        /// Select the closest Kris on X-axis
        /// </summary>
        /// <returns>X position of the closest Kris</returns>
        public float choixKrisX()
        {
            if (game.tabKris.GetLength(0) == 2)
            {
                if (game.tabKris[0].Hp == 0)
                    return game.tabKris[1].Position.X;
                else if (game.tabKris[1].Hp == 0)
                    return game.tabKris[0].Position.X;
                else
                {
                    if (Math.Abs(game.tabKris[0].Position.X - position.X) < Math.Abs(game.tabKris[1].Position.X - position.X))
                        return game.tabKris[0].Position.X;
                    else return game.tabKris[1].Position.X;
                }
            }
            else
                return (game.tabKris[0].Position.X);

        }

        /// <summary>
        /// Select the closest Kris on Y-axis
        /// </summary>
        /// <returns>Y position of the closest Kris</returns>
        public float choixKrisY()
        {
            if (game.tabKris.GetLength(0) == 2)
            {
                if (game.tabKris[0].Hp == 0)
                    return game.tabKris[1].Position.Y;
                else if (game.tabKris[1].Hp == 0)
                    return game.tabKris[0].Position.Y;
                else
                {
                    if (Math.Abs(game.tabKris[0].Position.X - position.X) < Math.Abs(game.tabKris[1].Position.X - position.X))
                        return game.tabKris[0].Position.Y;
                    else return game.tabKris[1].Position.Y;
                }
            }
            else
                return (game.tabKris[0].Position.Y);

        }

        /// <summary>
        /// Handle collision on Y-axis
        /// </summary>
        /// <returns></returns>
        public bool CollisionE()
        {
            int x1 = (int)(position.X / Plateforms.Plateform.Width), x2 = (int)((position.X + WIDTH) / Plateforms.Plateform.Width);
            int y = (int)(position.Y / Plateforms.Plateform.Height) + 1;
            if (x1 == x2)
                return (game.map.Plateforms[x1, y].Collision == Krist_em_up.Plateforms.TileCollision.Impassable);
            else
                return ((game.map.Plateforms[x1, y].Collision == Krist_em_up.Plateforms.TileCollision.Impassable) ||
                    (game.map.Plateforms[x2, y].Collision == Krist_em_up.Plateforms.TileCollision.Impassable));
        }
        public bool CollisionUp()
        {
            try
            {
                int x1 = (int)(position.X / Plateforms.Plateform.Width), x2 = (int)((position.X + WIDTH) / Plateforms.Plateform.Width);
                int y = (int)(position.Y / Plateforms.Plateform.Height) - 1;
                if (x1 == x2)
                    return (game.map.Plateforms[x1, y].Collision == Krist_em_up.Plateforms.TileCollision.Impassable);
                else
                    return ((game.map.Plateforms[x1, y].Collision == Krist_em_up.Plateforms.TileCollision.Impassable) ||
                        (game.map.Plateforms[x2, y].Collision == Krist_em_up.Plateforms.TileCollision.Impassable));
            }
            catch(Exception)
            {
                return false;
            }
        }
        /// <summary>
        /// Check if he is on screen
        /// </summary>
        /// <returns>Whether or not he is on screen</returns>
        public bool IsOnScreenE(Vector2 origin)
        {
            return (position.X >= origin.X && position.Y >= 0 && position.X + WIDTH <= game.game.graphics.PreferredBackBufferWidth + origin.X
                && position.Y + HEIGHT <= game.game.graphics.PreferredBackBufferHeight);
        }
        public float Width()
        {
            return texture.Width;
        }
        public float Height()
        {
            return texture.Height;
        }
        public state getState
        {
            get { return etat; }
        }

        public override void UpdateRectangle()
        {

        }
    }
}
